<?php
/**
 * Clase auxiliar para ordenar los transSets
 */  
class PFireableTransSet {
	/*
	 * fireable transSet
	 */
	public $fireableTS;
	/*
	 * condicion de eliminar
	 */
	public $deleteable;
}



class EFacade {
/**
	 * @type ENetwork
	 */
	private $engineNetwork;

	// ----------------------------------------
	// ----- Props
	// ----------------------------------------

	/**
	 * @type DefaultUserByRoleGetter
	 */

	private $userByRoleGetter;

	/**
	 * @type mixed
	 */
	private $customData;

	// ----------------------------------------
	// ----- Read Only Props
	// ----------------------------------------

	/**
	 * @type epManager
	 */
	public $session;

	/**
	 * @type MDocumentRoot
	 */
	protected $documentRoot;

	/**
	 * @type Network
	 */
	protected $network;

	/**
	 * @type WFActivty
	 */
	protected $workflow;
	
	/**
	 * @type WFActivityHist
	 */
	protected $wfHistory;
	
	/**
	 * @type MTransSet
	 */
	protected $transSet;

	/**
	 * @type MTransSet
	 */
	protected $allTransSet;

	/**
	 * @type MPlaceSet
	 */
	protected $placeSet;
	
	/**
	 * @type MRole
	 */
	protected $role;

	// ----------------------------------------

	private function __construct() {
		$this->userByRoleGetter = new DefaultUserByRoleGetter();
	}
	/**
	 * @return MDocumentRoot
	 */
	public function & getDocumentRoot() {
		return $this->documentRoot;
	}
	
	public function & getAllTransSet() {
		return $this->allTransSet;
	}

	/**
	 * @return WFActivity
	 */
	public function & getWorkflow() {
		return $this->workflow;
	}
	
	public function & getNetworkRef() {
		return $this->network;
	}
	
	public function & getPlaceSet() {
		return $this->placeSet;
	}
	
	public function & getCurrentTransSet($workflow, $role) {
		$curTransSetRole = array();
		$transSet = $workflow->docTypeRef->networkRef->fireableTransSet($workflow->markingVector, $role);	
		return $transSet;
	}
	
	/**
	 * @return userByRoleGetter
	 */
	public function & getUserByRoleGetter() {
		return $this->userByRoleGetter;
	}

	/**
	 * @param userByRoleGetter
	 * @return void
	 */
	public function setUserByRoleGetter(& $userByRoleGetter) {
		$this->userByRoleGetter = & $userByRoleGetter;
	}	


	public function & getCurrentPlaceSet($workflow) {
		$placeSet = $workflow->docTypeRef->networkRef->placeSetList($workflow->markingVector);
		return $placeSet[0];
	}

    
    public static function & initWorkflow(& $session, & $documentRoot, & $role) {
		$facade = & new EFacade();
		$facade->session = & $session;
		$facade->session->evictAll('MNetwork');
		$facade->session->evictAll('MTrans');
		$facade->session->evictAll('MPlace');
		$facade->session->evictAll('MTransSet');
		$facade->session->evictAll('MFireableTransSet');
		$facade->session->evictAll('MPlaceSet');
		$facade->session->evictAll('MPlace');
		$facade->session->evictAll('WActivity');
		
		$facade->role = & $role;
		$facade->init($documentRoot, $role);
		
		return $facade;
	}
	
	public static function & initWorkflowWithoutRole(& $session, & $documentRoot, $typeOfTrans) {
		$facade = & new EFacade();
		$facade->session = & $session;
		$facade->initWithoutRole($documentRoot, $typeOfTrans);
		
		return $facade;
	}

    public static function & initWorkflowForTimer(& $session, & $documentRoot) {
		$facade = & new EFacade();
		$facade->session = & $session;
		$facade->documentRoot = $documentRoot;
		$facade->workflow = & $documentRoot->getWorkflowRef();
		$facade->workflow->networkRef = $facade->workflow->docTypeRef->networkRef;
		$facade->allTransSet = $facade->workflow->fireableTransSet;
		$facade->placeSet = $facade->workflow->workPlace;		
		return $facade;
	}
	
	
	
	/**
	 * Funcion para crear un WorkFlow desde cero o cargar uno existente
	 */
	private function init(& $documentRoot, & $role) {
		/*
		 * Si no hay un rol definido, entonces se crea un Workflow, si no se carga.
		 */
		$this->documentRoot = $documentRoot;

		$name = $documentRoot->getDocumentName();
		

		if($role) {
			// se trabaja sobre un WorkFlow ya creado
			$this->workflow = & $documentRoot->getWorkflowRef();
			$this->workflow->networkRef = $this->workflow->docTypeRef->networkRef;
			$this->allTransSet = $this->workflow->fireableTransSet;
			$this->placeSet = $this->workflow->workPlace;
			foreach($this->allTransSet as $transSet) {
				if($transSet->transSetType == 0) {
					$this->transSet = $transSet;
					break;
				}
			}
		}
		else {
			
			$query = 'FROM MDocType as x WHERE x.name = ?';	
			$docType = $this->session->find($query, $documentRoot->getDocumentName());
			
			if (!$docType) {
				throw new Exception('No se puede cargar el doctype con nombre ' . $documentRoot->getDocumentName());
			}
			$docType = $docType[0];
			$this->network =  $docType->networkRef;
			$this->workflow = $this->session->create('WFActivity', $this->network);
			$this->workflow->docTypeRef =  $docType;
			$this->workflow->networkRef = $docType->networkRef;
			$this->workflow->document = $this->documentRoot;
			$this->workflow->initiate($this->network);
			$this->wfHistory = 	$this->session->create('WFActivityHist');
			$this->wfHistory->creationDate = time();
			$actor = $this->session->create('MActor');
			$actor->userRef = $documentRoot->usuarioActual;
			$actor->workflowRef = $this->workflow;
			$this->wfHistory->creatorActor = $actor;
			$this->workflow->history = $this->wfHistory;

			$allTransSet = $this->workflow->networkRef->allFireableTransSet($this->workflow->markingVector);
		
			$this->placeSet = $this->workflow->networkRef->placeSetList($this->workflow->markingVector);
	
			$this->workflow->workPlace = $this->placeSet;
			
			$this->transSet = $this->workflow->networkRef->fireableTransSet($this->workflow->markingVector,$role);

			foreach($allTransSet as $transS) {
				$firTS = $this->session->create('WFireableTransSet');
				$firTS->fireableDateAfter = 0;
				$firTS->lastDate = 0;
				$firTS->transSetType = $transS->transSetType;
				$firTS->roleRef = $transS->roleRef;
				$firTS->designedUser = NULL;
				$firTS->transSet = $transS;
				$fireableTransSet[] = $firTS;
			}
			
			$this->workflow->fireableTransSet = $fireableTransSet;			
			$this->allTransSet = $fireableTransSet;
			
		}
	}

	private function initWithoutRole(& $documentRoot, $typeOfTrans) {
			$this->documentRoot = & $documentRoot;
			$this->workflow = $this->session->create('WFActivity', $this->network);
			$this->workflow->document = $this->documentRoot;
			$this->workflow->initiate();
			$this->wfHistory = 	$this->session->create('WFActivityHist');
			$this->wfHistory->creationDate = time();
			$this->wfHistory->workflow = $this->workflow;
			$this->workflow = & $documentRoot->getWorkflowRef();
			$this->transSet = & getCurrentTransSetAuto($this->workflow, $typeOfTrans);
			$this->placeSet = & EFacade :: getCurrentPlaceSet($this->workflow);
	}
	
	
	public function fireTrans(& $modelTrans, & $givenUser = NULL, $notice = NULL, $setup = TRUE) {
				
		foreach($this->workflow->fireableTransSet as $fTrans) {
			$fTrans->delete();
		}
	
		$this->workflow->fireTransition($modelTrans);
		// execution of task asociated to the tfired transition

		$updateClass = trim($modelTrans->class,' ');
		$updateMethod = trim($modelTrans->methodClass,' ');
		if(!(strlen($updateClass) <=1 || strlen($updateMethod) <=1)) {
			$doc = $this->workflow->document;
			$update = new $updateClass;
			$update->$updateMethod($doc);
		}
		
		

		
		$allTransSet = $this->workflow->networkRef->allFireableTransSet($this->workflow->markingVector);

		$fireableTransSet = array();

		foreach($allTransSet as  $transS) {
			$firTS = $this->session->create('WFireableTransSet');
			$firTS->fireableDateAfter = 0;
			$firTS->lastDate = 0;
			$firTS->transSetType = $transS->transSetType;
			$firTS->roleRef = $transS->roleRef;
			$firTS->designedUser = NULL;
			$firTS->transSet = $transS;
			$fireableTransSet[] = $firTS;
		}
				
		$this->allTransSet  = $fireableTransSet;
		
		
		$this->workflow->fireableTransSet = $fireableTransSet;
		
		$actor = $this->session->create('MActor');

		$newFiredTrans = $this->session->create('WWorkHistory');
		$newFiredTrans->date = time();
		$newFiredTrans->firedTransition = $modelTrans;
		$newFiredTrans->userRef =$givenUser;
		$newFiredTrans->actor = $actor;
		$newFiredTrans->activity = $this->workflow->history;
		$actor->userRef = $givenUser;
		$actor->workflowRef = $this->workflow;
		$this->workflow->history->history[] = $newFiredTrans;
		$newFiredTrans->commit();
		$this->placeSet = $this->workflow->workPlace;
		$actor->commit();
		$this->workflow->commit();
		$this->documentRoot->commit();


		
		// preparar los transsets
		
		if ($setup) {	
//			$this->setupAutoTrans();
//			$this->setupTimeTrans();
			$this->setupDelegator();
		}
	}

	protected function setupAutoTrans() {

		$transSet = array();
		foreach($this->allTransSet as $fTransSet) {
			if($fTransSet->transSetType == 1 ) {
				$transSet[0] = $fTransSet;
			}
		}
		
		foreach($this->allTransSet as $fTransSet) {
			if($fTransSet->transSetType == 2 ) {
				$transSet[1] = $fTransSet;
			}
		}
		
		if ($transSet[0] != NULL) {

			foreach ($transSet[0]->transList as $modelTrans) {
				if ($modelTrans->getType() == MTrans :: $TYPE_AUTO_SYSTEM) {
					$null = NULL;
					$this->fireTrans($modelTrans, $null, 'Transaccion disparada por el Sistema', FALSE);
					$transFired = TRUE;
				}
			}
		}

		if ($transSet[1] != NULL) {
			// -----------------------------------------
			// ejecuto el agente que decide que transicion disparar
			// -----------------------------------------
			$agentClass = trim($transSet[1]->getAgentClass(), ' ');

			if (!($agentMethods = & $transSet[1]->getAgentMethods())) {
				throw new Exception('No existe un metodo expecificado para el agente. archivo ' . __FILE__ . ' linea ' . __LINE__);
			}

			$agentMethods = explode(',', $agentMethods);
			$agente = new $agentClass ();

			foreach ($agentMethods as $methodName) {

				$method = trim($methodName, ' ');
				$agente-> $method ($this);
			}

		}

	}

	/**
	 * @return void
	 */
	protected function setupDelegator() {
		$this->session->evictAll('MTransSet');

		foreach($this->workflow->fireableTransSet as & $fTransSet) {

			if($fTransSet->transSet->transSetType == 0 ) {
				
				$delegatorClass = trim($fTransSet->transSet->delegatorClass,' ');

			// -----------------------------------------
			// ejecuto el agente que decide que transicion disparar
			// -----------------------------------------
						
			
				if (!($delegatorMethods = & $fTransSet->transSet->delegatorMethods) and $delegatorClass) {
					throw new Exception('No existe un metodo expecificado para el delegador'.$fTransSet->transSet->delegatorMethods. 'archivo ' . __FILE__ . ' linea ' . __LINE__);
				}
				$method = $fTransSet->transSet->delegatorMethods;
			
				$delegatorMethod = trim($method);
			
				$delegator = new $delegatorClass;
	
				$user = $delegator-> $method ($this,$fTransSet->transSet,$method);
				$fTransSet->designedUser = $user;			
				$fTransSet->commit();
				
			}
			if($fTransSet->transSet->transSetType == 4) {
				$delegatorClass = trim($fTransSet->transSet->delegatorClass,' ');
			
			// -----------------------------------------
			// ejecuto el agente que decide que transicion disparar
			// -----------------------------------------
			
				if (!($delegatorMethods = & $fTransSet->transSet->delegatorMethods) and $delegatorClass) {
					throw new Exception('No existe un metodo expecificado para el delegador'.$fTransSet->transSet->delegatorMethods. 'archivo ' . __FILE__ . ' linea ' . __LINE__);
				}
				$method = $fTransSet->transSet->delegatorMethods;			
				$delegatorMethod = trim($method);
				$delegator = new $delegatorClass;
				$tiempo = $delegator-> $method ($this,$fTransSet->transSet,$method);
				$fTransSet->fireableDateAfter = $tiempo;
				$fTransSet->commit();
			}
		}
	}
	
	
	public function saveWorkflow() {
		
		$query = 'FROM MDocType as x WHERE x.name = ?';

		$docType = $this->session->find($query, $this->documentRoot->getDocumentName());
		

		if (!$docType) {
			throw new Exception('No se puede cargar el doctype con nombre ' . $documentRoot->getDocumentName());
		}

		$docType = $docType[0];

		// ----------------------------------------
		// Get network
		// ----------------------------------------

		$modelNetwork = & $docType->getNetworkRef();
	
		$this->workflow->setNetworkRef($modelNetwork);

		$allTransSet = $this->workflow->networkRef->allFireableTransSet($this->workflow->markingVector);

		// ----------------------------------------
		// Set trans relation ships
		// ----------------------------------------

		$this->workflow->setDocumentRootRef($this->documentRoot);
		$this->documentRoot->setWorkflowRef($this->workflow);

	}
	
	public function saveFirstWorkflow() {
		
		$query = 'FROM MDocType as x WHERE x.name = ?';

		$docType = $this->session->find($query, $this->documentRoot->getDocumentName());
		

		if (!$docType) {
			throw new Exception('No se puede cargar el doctype con nombre ' . $documentRoot->getDocumentName());
		}

		$docType = $docType[0];

		// ----------------------------------------
		// Get network
		// ----------------------------------------

		$modelNetwork = & $docType->getNetworkRef();
	
		$this->workflow->setNetworkRef($modelNetwork);
		$this->workflow->docTypeRef = $docType;
		

		$allTransSet = $this->workflow->networkRef->allFireableTransSet($this->workflow->markingVector);

		// ----------------------------------------
		// Set trans relation ships
		// ----------------------------------------

		$this->workflow->setDocumentRootRef($this->documentRoot);
		$this->documentRoot->setWorkflowRef($this->workflow);
		$this->workflow->history->creatorActor->userRef = $this->documentRoot->usuarioActual;
		$this->documentRoot->commit();
		$this->workflow->commit();
	}
	

	/**
	 * @param MTransSet
	 * @param string
	 * @return int
	 */
	public static function getFieldStateByTransSet(& $transSet, $fieldName) {
		
		$docFieldStateList = & $transSet->getDocFieldStateList();
		foreach ($docFieldStateList as $key => & $docFieldState) {
			if ($fieldName === $docFieldState->getDocFieldRef()->getName()) {
				return $docFieldState->getState();
			}
		}

		throw new Exception('No se encuentra el campo : ' . $fieldName);
	}

}
?>